
Screenshot: Preparing for Battle
文件:
•. cannon.rb
•. t8.rb
•. lcdrange.rb
在这个示例中,我们引入第一个可以绘制自身的自定义部件。我们还加入了一个狠有用的键盘接口(仅仅用了两行代码)。
这个文件与第7章中的lcdrange.rb 非常类似。我们新加入了一个信号槽: setRange()。
现在,我们加入了设置LCDRange 的范围的功能。在此之前,它的范围都固定为0到99。
def setRange(minVal, maxVal)
if minVal < 0 || maxVal > 99 || minVal > maxVal
qWarning("LCDRange::setRange(#{minVal}, #{maxVal})\n" +
"\tRange must be 0..99\n" +
"\tand minVal must not be greater than maxVal")
return
end
@slider.setRange(minVal, maxVal)
end
setRange()信号槽会设置LCDRange 中的滑动器的范围。因为我们已经将Qt::LCDNumber设置为只显示两位数字,所以,我们在这里限制minVal 和maxVal 的范围,以避免Qt::LCDNumber发生溢出。(我们可以把取值范围再放宽一点,让最小的可能取值为-9,不过我们没这么做。)如果所设置的参数是不被允许的,那么,我们使用Qt的QtGlobal::qWarning()函数来向用户发出一个警告,并且立即返回。QtGlobal::qWarning()是一个类似printf的函数,默认情况下会将它的输出内容输出到$stderr。如果有需要的话,妳可以使用QtGlobal::qInstallMsgHandler()来安装妳自己的处理函数。
lcd.setSegmentStyle(Qt::LCDNumber::Filled)
这使得我们的液晶显示器数字部件看起来更好看。有一点,我不太肯定,我认为,是在设置了一个调色板(参考下一小节)之后才使得这句代码能够起到作用的。我只知道,在之前的章节中,这句代码不起作用,在本章的代码中才起作用。
@currentAngle = 45
setPalette(Qt::Palette.new(Qt::Color.new(250, 250, 200)))
setAutoFillBackground(true)
构造函数中将角度值初始化为45度,并且为这个部件设置了一个自定义的调色板。
这个调色板会使用指定的颜色作为背景色,并且为其它内容选择适当的颜色。(对于这个部件,只有背景色和文字颜色会被实际使用。)然后,我们调用setAutoFillBackground(true),以告诉Qt来自动填充背景色。
Qt::Color是以一个RGB(红-绿-蓝)三元组的形式来表示的,其中每个值的范围都是从0 (暗)到255 (亮)。我们还可以使用预定义的颜色,例如Qt::yellow,而不用指定一个RGB值。
def setAngle(angle)
if angle < 5
angle = 5
elsif angle > 70
angle = 70
end
if @currentAngle == angle
return
end
@currentAngle = angle
update()
emit angleChanged(@currentAngle)
end def setAngle(degrees)
这个函数用于设置角度值。我们选定的有效范围是5 到70 ,并且相应地调整指定的角度度数。这次,当新设置的角度超出了允许的范围的时候,我们不发出警告。
如果新设置的角度与旧的角度相等,则我们立即返回。这一点狠重要,只有当角度值真的发生改变时才发射angleChanged()信号。
然后我们设置新的角度值,并且重绘我们的这个部件。Qt::Widget::update()函数会清空该部件的内容(通常是使用背景色来填充它)然后向该部件发送一个绘图事件。这将导致调用该部件的绘图事件处理函数。
最后,我们发射angleChanged()信号,告诉外部世界说角度已经发生改变了。emit这个关键字是Qt独有的,并不是普通的Ruby语法。
def paintEvent(event)
painter = Qt::Painter.new(self)
painter.drawText(200, 200, tr("Angle = #{@currentAngle}"))
painter.end()
end
这是我们第一次尝试编写一个绘图事件处理器。event参数中含有对于当前的绘图事件的说明信息。Qt::PaintEvent包含了该部件中必须更新的区域信息。目前,我们偷点懒,直接绘制所有内容。
我们的代码会在部件中一个固定的位置显示出角度值。我们创建一个在这个部件上操作的Qt::Painter,并且使用它来绘制一个字符串。我们日后还会再研究Qt::Painter;它能做好多事情。
angle = LCDRange.new()
angle.setRange(5, 70)
在这个构造函数里,我们创建并且设置好LCDRange部件。我们将这个LCDRange 设置为接受从5到70度的角度。
cannonField = CannonField.new()
我们创建CannonField部件。
connect(angle, SIGNAL('valueChanged(int)'),
cannonField, SLOT('setAngle(int)'))
connect(cannonField, SIGNAL('angleChanged(int)'),
angle, SLOT('setValue(int)'))
这里,我们将LCDRange 的valueChanged()信号连接到CannonField 的setAngle() 信号槽。这会导致,当用户操作LCDRange 时,就会更新 CannonField 的角度值。我们还做了反方向的连接,这样,在CannonField 中发生的角度值变化也会更新LCDRange 中的值。在我们的这个示例中,我们不会直接改变CannonField 的角度值;但是,通过最后的那个connect()语句,我们可以确保,未来对代码的修改不会破坏那两个部件的值之间的同步关系。
这些代码展示了组件式编程和适当封装的威力。
注意,这一点狠重要,只有当角度值实际上发生改变时才发射angleChanged()信号。如果LCDRange和CannonField都省略掉了这个检查的话,那么,当其中一个值发生一次改变时,整个程序就会进入一个无限循环。
gridLayout = Qt::GridLayout.new()
到目前为止,我们曾经使用过Qt::VBoxLayout来管理几何属性。不过,现在我们想要更好地控制整个布局,所以我们换用更强大的Qt::GridLayout类。Qt::GridLayout并不是一个部件;它是一个特殊的类,可用于管理任何部件的子代部件。
我们不需要在Qt::GridLayout的构造函数中指定任何的维度信息。Qt::GridLayout会根据我们填充进去的网格单元格来确定行数和列数。
|
|
|
上图显示了我们试图实现的布局效果。左侧显示出布局的图解;右侧显示出程序的实现截屏。(这两张图片的版权属于奇趣公司。)
gridLayout.addWidget(quit, 0, 0)
我们将Quit按钮添加到网格中的左上角,即,坐标为(0, 0)的那个单元格中。
gridLayout.addWidget(angle, 1, 0)
我们把用于显示角度的LCDRange放置在单元格(1, 0)中。
gridLayout.addWidget(cannonField, 1, 1, 2, 1)
我们让CannonField对象占用单元格(1, 1)和单元格(2, 1)。
gridLayout.setColumnStretch(1, 10)
我们告诉Qt::GridLayout,右边的那一列(列号为1)是可拉伸的,其拉伸因子是10。因为左边的列是不可拉伸的(它的拉伸因子是0,即为默认值),所以,当MyWidget 被改变大小时,Qt::GridLayout会尝试让左边的那些部件保持不变的大小,而只相应地改变CannonField 的大小。
在这个特定的示例时,给列号为1的那一列赋予任意一个大于0的拉伸因子都会产生相同的效果。在一个更复杂的布局中,妳可以使用拉伸因子来让特定的列或行以两倍于另一个列或行的速度来拉伸,只需赋予适当的拉伸因子就可以了。
angle.setValue(60)
我们设置一个初始的角度值。注意,这句代码会触发LCDRange 到 CannonField 之间的信号连接。
angle.setFocus()
我们的最后一个动作就是让angle 拥有键盘焦点,这样,键盘上的输入事件就会默认被那个LCDRange 部件接收了。
当滑动器被拖动时,CannonField部件会显示出新的角度值。改变窗口大小时,CannonField部件会被给予尽可能多的空间。
试着改变窗口的大小。如果妳把窗口弄得特别窄或者特别矮,会发生什么?
如果妳给左边的一列赋予一个非0的拉伸因子的话,那么,当妳改变窗口大小时会发生什么?
试着将"Quit"改成"&Quit"。按钮的外观会变成什么样的?(至于它究竟会不会发生改变,这最终取决于不同的平台。)当程序正在运行的时候,妳按Alt+Q,结果如何?
将文字在CannonField里居中。
神创天下 与妲己共洗鸳鸯浴
未知美人
HxLauncher: Launch Android applications by voice commands